'''
Created on 2010/07/06

@author: yukiyonezawa
'''
from pyrfid.DataProtocol.DSFID.AccessMethod.AccessMethod import AccessMethod
from xml.dom import minidom
from pyrfid.Util.Util import Util
from pyrfid.DataProtocol.DSFID.DataFormat.DataFormatUtil import DataFormatUtil
import sys
from pyrfid.DataProtocol.LogicalMemory.DataSet import DataSet
from pyrfid.asn1.Encoder import Encoder
from pyrfid.DataProtocol.DSFID.Processor.Compactor import Compactor
from pyrfid.DataProtocol.CommandResponseUnit.ReadObjectsCRU import ReadObjectsCRU
from pyrfid.DataProtocol.AddObject import AddObject

class TagDataProfile(AccessMethod):
    '''
    classdocs
    '''
    ACCESS_METHOD_ID = 3;
    __id_table = dict()
    driver = object()
    
    ALLOW_COMPACTION_TYPE = [AddObject.APPLICATION_DEFINED, AddObject.COMPACT, AddObject.UTF8_DATA]
    
    
    def __init__(self):
        '''
        Constructor
        '''
        self.__id_table["K_Version"] = ""
        self.__id_table["K_Interpretation"] = ""
        self.__id_table["K_Root_OID"] = ""
        self.__id_table["K_ProfileID"] = ""
        self.__id_table["K_TableEnd"] = ""
        self.__id_table["IDTable"] = list()        

    def setDriver(self, driver):
        self.driver = driver
        
    def readAndAnalyzeProfileHeader(self, tag_id, read_start_address):
        
        read_address = read_start_address 
        dspid = self.driver.readUserMemory(tag_id, read_address, 1)
        print dspid
        read_address += 1 
        length = self.driver.readUserMemory(tag_id, read_address, 1)
        print length
        read_address += 1
        other_data = self.driver.readUserMemory(tag_id, read_address, int(length, 16) - len(dspid) / 2)
        print other_data
        lock_block_size = other_data[:2]
        include_size_of_data_set = other_data[2:]
        all_data_size = ""
        for data_index in range(len(include_size_of_data_set) / 2):
            data_hex = include_size_of_data_set[data_index*2:data_index*2 + 2]
            if data_hex == "80":
                break
            all_data_size += data_hex 
        
        return {"profile_header":dspid + length + other_data, "data_size":int(all_data_size, 16), "lock_block_size":lock_block_size}
    
    def addData(self, tag_id, dsfid_on_tag, data_format, add_objects_list):
        #@todo: implements profile id
        profile_id = 123
        
        #read profile Table 
        try:
            self.__loadProfileTable(data_format.DATA_FORMAT_ID, profile_id)
        except:
            raise "PROFILE FILE NOT FOUND:DSFID("+str(data_format.DATA_FORMAT_ID)+")"
        
        all_data_set_on_tag = self.__getDataSetFromIdTable()
        all_data_set_list = data_format.formattingDataSet([], all_data_set_on_tag)

        add_data_set_list = DataFormatUtil.convertDataSet(add_objects_list)
        add_data_set_list = data_format.formattingDataSet([], add_data_set_list)
        
        profile_header = self.__profileHeaderCalculator(0, profile_id, self.driver.getLockSize(), all_data_set_list)
        
        if dsfid_on_tag == "":
            #New Tag
            self.driver.writeUserMemory(tag_id, 0, profile_header)
        else: 
            profile_header_on_tag = self.readAndAnalyzeProfileHeader(tag_id, 0)
            if profile_header != profile_header_on_tag["profile_header"]:
                raise "Not Match Profile Header "
        
        write_data_set_and_write_address_list = list()
        for write_data_set in add_data_set_list:
            
            write_address = len(profile_header) / 2
            is_writeable_data_set = False
            
            
            for assume_data_set in all_data_set_list:
                if (write_data_set.PRECURSOR + write_data_set.OBJECT_IDENTIFIER) == (assume_data_set.PRECURSOR + assume_data_set.OBJECT_IDENTIFIER):
                    is_writeable_data_set = True
                    break
                write_address += len(assume_data_set.getLogicalMemory()) / 2
            
            if is_writeable_data_set == False:
                raise "Data Set can not write"
            write_data_set_and_write_address_list.append([write_data_set, write_address])
        
        for write_data_set_and_write_address in write_data_set_and_write_address_list:
            self.driver.writeUserMemory(tag_id, write_data_set_and_write_address[1], write_data_set_and_write_address[0].getLogicalMemory())
        
        if dsfid_on_tag != "":
            return True
        
        #formatting
        write_address = 0 + len(profile_header) / 2
        
        for assume_data_set in all_data_set_list:
            is_writed_data_set = True
            for write_data_set in add_data_set_list:
                if (write_data_set.PRECURSOR + write_data_set.OBJECT_IDENTIFIER) == (assume_data_set.PRECURSOR + assume_data_set.OBJECT_IDENTIFIER):
                    is_writed_data_set = False
                    break
            
            if is_writed_data_set == False:
                write_address +=  len(assume_data_set.getLogicalMemory()) / 2
                continue
            
            self.driver.writeUserMemory(tag_id, write_address, len(assume_data_set.getLogicalMemory()) / 2 * "80" )
            write_address +=  len(assume_data_set.getLogicalMemory()) / 2

    def readData(self, tag_id, data_format, read_type):
        #@todo: implements profile id
        profile_id = 123
        
        #read profile Table 
        try:
            self.__loadProfileTable(data_format.DATA_FORMAT_ID, profile_id)
        except:
            raise "PROFILE FILE NOT FOUND:DSFID("+str(data_format.DATA_FORMAT_ID)+")"
        
        all_data_set_on_tag = self.__getDataSetFromIdTable()                
        all_data_set_list = data_format.formattingDataSet([], all_data_set_on_tag)                
        profile_header = self.__profileHeaderCalculator(0, profile_id, self.driver.getLockSize(), all_data_set_list)
                
        existing_data_set = []
        start_address = len(profile_header) / 2
        
        for data_set in all_data_set_list:
            object_prefix = Util.convertBitToHex(data_set.PRECURSOR) + data_set.OFFSET +  data_set.LENGTH_OF_OBJECT_IDENTIFIER + data_set.OBJECT_IDENTIFIER +  data_set.LENGTH_OF_OBJECT
            start_address += len(object_prefix) / 2
            read_data = self.driver.readUserMemory(tag_id, start_address, len(data_set.OBJECT) / 2)
            
            data_set.OBJECT = read_data
            start_address += len(read_data) / 2
            start_address += len(data_set.PAD) / 2            
            
            if read_data != "80" * (len(data_set.OBJECT) / 2):
                existing_data_set.append(data_set)
                
        add_object_list = data_format.decodeDataSetToAddData(existing_data_set)
        return add_object_list

        
    def __getDataSetFromIdTable(self):
        all_data_set = list()
        base_oid_urn = self.__id_table['K_Root_OID'][8:]
        base_oid = base_oid_urn.replace(".", " ")
        
        for id_info in self.__id_table["IDTable"]:
            data_set = DataSet()
            data_set.OBJECT_IDENTIFIER = Encoder.oidEncode(base_oid+" "+id_info["OID"])
            data_set.OBJECT = int(id_info["EncodeLength"]) * "80"
            data_set.LENGTH_OF_OBJECT = Compactor.getObjectLength(data_set.OBJECT)
            data_set.PRECURSOR = "0"+id_info["CompactCode"]+"0000"
            all_data_set.append(data_set)
        
        return all_data_set
    
    def __profileHeaderCalculator(self, start_data_set_address, profile_id, lock_block_size, all_data_set):
        """ for getProfileHeader
            dataFormat         -- int,
            profile_id         -- int,
            blockSize(by byte) -- int,
            return DSPID + headerLength + lockBlockSizeHex + allDataSizeHex + padding
        """
        #get DSFID(from accessMthod & formatId)
        # DSFID                
        #get DSPID(from profileId)
        dspid = Util.convertIntToHex(profile_id, 2)
    
#        DSPID = Processor.encodeIntToHex(self, profileId)
        
        #get write Block Size(from blockSize)
        lock_block_size_hex = Util.convertIntToHex(lock_block_size, 2)

        #get All Data Size(from allDataSize)
        #@todo: is need?
#        headerLength = lock_block_size - len(dsfid.getDSFIDLogicalMemoryMap()) / 2
        
        assumeAllDataSizeHex = '00' #byte
        #get Padding by Block Size
        
        #Annex P.2
        length_all_data_set = 0
        for data_set in all_data_set:
            length_all_data_set += len(data_set.getLogicalMemory()) / 2
        
        paddingReminder = (start_data_set_address + len(dspid) / 2 + len(lock_block_size_hex) / 2 + len(assumeAllDataSizeHex) / 2 + 1 ) % lock_block_size # 1 is HeaderLength
        padding = '80' * (lock_block_size - paddingReminder)

        #get Header Length(byte)
        #@todo: is need necessary
        profile_header_length = Util.convertIntToHex(len(dspid) / 2 + len(lock_block_size_hex) / 2 + len(assumeAllDataSizeHex) / 2 + len(padding) / 2)# 1 is HeaderLength
        
        #all Data Size is (DSFID) + (Profile Header) + Data-Sets By Byte
        size_of_all_data_set = 0
        for data_set in all_data_set:
            size_of_all_data_set += len(data_set.getLogicalMemory()) / 2
        
        all_data_size_hex = Util.convertIntToHex( len(dspid) / 2 + int(profile_header_length, 16) + size_of_all_data_set)
        
        profile_header = dspid + profile_header_length + lock_block_size_hex + all_data_size_hex + padding
        
        #@todo: is need?
#        while len(profileHeader) / 2 != int(header_length, 16):
#            paddingReminder = (start_data_set_address + len(dspid) / 2 + len(lock_block_size_hex) / 2 + size_of_all_data_set + 1 ) % lock_block_size # 1 is HeaderLength
#            padding = '80' * (lock_block_size - paddingReminder)
#            header_length = Util.convertIntToHex(len(dspid) / 2 + len(lock_block_size_hex) / 2 + size_of_all_data_set + len(padding) / 2 + 1)# 1 is HeaderLength
#            all_data_size_hex = Util.convertIntToHex(int(header_length, 16) + size_of_all_data_set)
#            # print allDataSize
#            profileHeader = dspid + header_length + lock_block_size_hex + all_data_size_hex + padding
        
        return profile_header
    
    
    def __loadProfileTable(self, format_id, profile_id):        
        """docstring for buildIDTable"""
        
        table_id = "F" + str(format_id) + "P"+str(profile_id)
        
        filePath = './' + table_id + '.xml'
        
        tree = minidom.parse(filePath)
        
        kVersion = tree.getElementsByTagName('K-Version')
        if kVersion != "":
            self.__id_table["K_Version"] = kVersion[0].firstChild.data
#            self.idTable.K_Version = kVersion[0].firstChild.data
        
        kTableID = tree.getElementsByTagName('K-Interpretation')
        if kTableID:
            self.__id_table["K_Interpretation"] = kTableID[0].firstChild.data
#            self.idTable.K_Interpretation = kTableID[0].firstChild.data
        
        kRootOID = tree.getElementsByTagName('K-Root-OID')
        if kRootOID:
            self.__id_table["K_Root_OID"] = kRootOID[0].firstChild.data
#            self.idTable.K_Root_OID = kRootOID[0].firstChild.data
        
        kIDSize = tree.getElementsByTagName('K-ProfileID')
        if kIDSize:
            self.__id_table["K_ProfileID"] = kIDSize[0].firstChild.data
#            self.idTable.K_ProfileID = kIDSize[0].firstChild.data
        
        kTableEnd = tree.getElementsByTagName('K-TableEnd')
        if kTableEnd:
            self.__id_table["K_TableEnd"] = kTableEnd[0].firstChild.data
#            self.idTable.K_TableEnd = kTableEnd[0].firstChild.data
        
        idTree = tree.getElementsByTagName('OIDList')
        
        if idTree:
            infos = []
            for id in idTree[0].childNodes:
                if id.childNodes.length != 0:
#                    idInfo = IDInfo()
                    id_info = dict()
                    idValue = id.getElementsByTagName('IDValue')
                    if idValue:
                        id_info["IDValue"] = idValue[0].firstChild.data
                                        
                    oidValue = id.getElementsByTagName('Relative-OID')
                    if oidValue:
                        id_info["OID"] = oidValue[0].firstChild.data
                    
                    lockDataValue = id.getElementsByTagName('LockData')
                    if lockDataValue:
                        id_info["LockData"] = lockDataValue[0].firstChild.data
                    
                    blockAlignValue = id.getElementsByTagName('BlockAlign')
                    if blockAlignValue:
                        id_info["BlockAlign"] = blockAlignValue[0].firstChild.data
                    
                    formatStringValue = id.getElementsByTagName('FormatString')
                    if formatStringValue:
                        id_info["FormatString"] = formatStringValue[0].firstChild.data
                    
                    compactCodeValue = id.getElementsByTagName('CompactCode')
                    if compactCodeValue:
                        id_info["CompactCode"] = compactCodeValue[0].firstChild.data
                    
                    encodeLengthValue = id.getElementsByTagName('EncodeLength')
                    if encodeLengthValue:
                        id_info["EncodeLength"] = int(encodeLengthValue[0].firstChild.data)
                    
                    infos.append(id_info)
            self.__id_table["IDTable"] = infos


